home *** CD-ROM | disk | FTP | other *** search
- /*
- *
- * g c . c -- Mark and Sweep Garbage Collector
- *
- *
- * Copyright ⌐ 1993-1996 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
- *
- *
- * Permission to use, copy, and/or distribute this software and its
- * documentation for any purpose and without fee is hereby granted, provided
- * that both the above copyright notice and this permission notice appear in
- * all copies and derived works. Fees for distribution or use of this
- * software or derived works may only be charged with express written
- * permission of the copyright holder.
- * This software is provided ``as is'' without express or implied warranty.
- *
- * This software is a derivative work of other copyrighted softwares; the
- * copyright notices of these softwares are placed in the file COPYRIGHTS
- *
- *
- * Author: Erick Gallesio [eg@unice.fr]
- * Creation date: 17-Feb-1993 12:27
- * Last file update: 11-Jun-1996 21:54
- *
- */
-
- #include "stk.h"
- #include "gc.h"
- #ifdef USE_STKLOS
- # include "stklos.h"
- #endif
- #ifdef USE_TK
- # include "tk-glue.h"
- #endif
-
-
- #define MIN_HEAP 100 /* A too small value cause an infinite loop */
- #define VALID_ADDRESS(heap_org, p) \
- ((p >= heap_org) && (p < heap_org + heap_size) && \
- (((((char *)p) - ((char *)heap_org)) % sizeof(struct obj)) == 0))
-
- #define HEAPS_INCREMENT 10
-
- #define gc_mark(ptr) STk_gc_mark(ptr)
-
- struct gc_protected {
- SCM *location;
- struct gc_protected *next;
- };
-
-
- /* exported vars */
- SCM STk_freelist;
- SCM *STk_stack_start_ptr;
- double STk_total_gc_time = 0.0;
- long STk_alloc_cells;
- int STk_gc_requested = 0;
-
- /* internal vars */
- static jmp_buf save_regs_gc_mark;
- static long gc_cells_collected;
- static long heap_size = INITIAL_HEAP_SIZE;
- static int gc_verbose = 0;
- static int gc_calls = 0;
- static SCM *heaps = NULL;
- static int heaps_length = 0;
- static int heaps_used = 0;
- static double time_gc_start;
-
- static struct gc_protected *protected_registers = NULL;
-
- static no_memory(void)
- {
- STk_panic("**** No more memory. Cannot allocate a new heap. Stop\n");
- }
-
- static void allocate_new_heap(void)
- {
- SCM ptr, next;
- SCM heap_org, heap_end;
-
- /* Don't use must_malloc here since it can conduct to call GC when
- * allocating large heaps (typically with large -cells xxx)
- */
-
- if (heaps_used == heaps_length) {
- /* Realloc heaps */
- heaps_length += HEAPS_INCREMENT;
- heaps = (heaps_used) ? realloc(heaps, heaps_length*sizeof(SCM)):
- malloc(heaps_length*sizeof(SCM));
- if (!heaps) no_memory();
- }
- ptr = (SCM) malloc(sizeof(struct obj)*heap_size);
- if (ptr) {
- heap_org = heaps[heaps_used++] = ptr;
- heap_end = heap_org + heap_size;
- }
- else
- no_memory();
-
- /* Prepare heap space */
- for(ptr = heap_org, next=ptr+1; ptr < heap_end; ptr=next, next=ptr+1) {
- ptr->type = tc_free_cell;
- ptr->cell_info = 0;
- ptr->gc_mark = 0;
- CDR(ptr) = (next < heap_end) ? next : STk_freelist;
- }
- STk_freelist = heap_org;
-
- if (gc_verbose)
- fprintf(STk_stderr, ";; [new heap allocated (%d/%d)]\n",
- heaps_used, heaps_length);
- }
-
- static void gc_start(void)
- {
- time_gc_start = STk_my_time();
- gc_calls += 1;
- gc_cells_collected = 0;
- gc_verbose = (VCELL(Intern(GC_VERBOSE)) != Ntruth);
-
- if (gc_verbose) fprintf(STk_stderr, ";; [starting GC]\n");
- }
-
- static void gc_end(void)
- {
- long total_cells, used_cells;
- double time_for_this_gc;
-
- total_cells = heaps_used * heap_size;
- used_cells = total_cells - gc_cells_collected;
-
- time_for_this_gc = STk_my_time() - time_gc_start;
- STk_total_gc_time += time_for_this_gc;
-
- /*
- * If heap is more than 75% filled after gc, allocate a new heap to
- * avoid continuous GCs
- */
- if (((float) used_cells / total_cells) > 0.75) allocate_new_heap();
-
- STk_gc_requested = 0;
-
- if (gc_verbose)
- fprintf(STk_stderr, ";; [end of GC (cells used: %ld/%ld; time: %.2fms)]\n",
- used_cells, total_cells, time_for_this_gc);
- STk_handle_signal(SIGHADGC);
- }
-
-
- void STk_gc_count_cells(long *allocated, long *used, long* calls)
- {
- register SCM ptr, heap_org, heap_end;
- register long used_cells = 0L;
- int i;
-
- for (i=0; i < heaps_used; i++) {
- heap_org = heaps[i];
- heap_end = heap_org + heap_size;
-
- for(ptr = heap_org; ptr < heap_end; ptr++)
- if (NTYPEP(ptr, tc_free_cell)) used_cells += 1;
- }
-
- *allocated = heaps_used * heap_size;
- *used = used_cells;
- *calls = (long) gc_calls;
- }
-
-
-
-
- int STk_valid_address(SCM p) /* True if p is a valid address. Used for #Pxyz */
- {
- int i;
-
- for(i=0; i < heaps_used; i++) {
- register SCM heap_org=heaps[i];
-
- if (VALID_ADDRESS(heap_org, p)) return TRUE;
- }
-
- return FALSE;
- }
-
-
- void STk_gc_mark(SCM ptr)
- {
- Top:
- if (NULLP(ptr) || SMALL_CSTP(ptr)) return;
- if (ptr->gc_mark) return;
-
- ptr->gc_mark = GC_MARK;
-
- switch (TYPE(ptr)) {
- case tc_nil: return;
- case tc_cons: gc_mark(CAR(ptr));ptr = CDR(ptr); goto Top;
- case tc_flonum: return;
- case tc_integer: return;
- case tc_bignum: return;
- case tc_symbol: ptr = VCELL(ptr);goto Top;
- case tc_keyword: return;
- case tc_subr_0: return;
- case tc_subr_1: return;
- case tc_subr_2: return;
- case tc_subr_3: return;
- case tc_subr_0_or_1: return;
- case tc_subr_1_or_2: return;
- case tc_subr_2_or_3: return;
- case tc_lsubr: return;
- case tc_ssubr: return;
- case tc_fsubr: return;
- case tc_syntax: return;
- case tc_closure: gc_mark(ptr->storage_as.closure.code);
- ptr = ptr->storage_as.closure.env;
- goto Top;
- case tc_free_cell: /* -----> Error */
- case tc_char: return;
- case tc_string: return;
- case tc_vector: {
- long j;
- for(j = 0;j < ptr->storage_as.vector.dim; j++)
- gc_mark(ptr->storage_as.vector.data[j]);
- return;
- }
- case tc_eof: return;
- case tc_undefined: return;
- case tc_iport:
- case tc_oport: gc_mark(PORT_REVENT(ptr));
- ptr = PORT_WEVENT(ptr);
- goto Top;
- case tc_isport: return;
- case tc_osport: return;
- case tc_boolean: return;
- case tc_macro: ptr = ptr->storage_as.macro.code; goto Top;
- case tc_localvar: ptr = ptr->storage_as.localvar.symbol; goto Top;
- case tc_globalvar: ptr = VCELL(ptr); goto Top;
- case tc_cont: ptr = STk_mark_continuation(ptr);
- goto Top;
- case tc_env:
- case tc_address: ptr = ptr->storage_as.env.data;
- goto Top;
- case tc_autoload: ptr = CAR(ptr);
- goto Top;
- case tc_Cpointer: return;
- #ifdef USE_STKLOS
- case tc_instance: {
- /* ACCESSORS_OF(ptr) doesn't need to be marked since it
- * is always accessible from SLOTS_OF(CLASS_OF(ptr))
- */
- long j;
-
- gc_mark(CLASS_OF(ptr));
- for (j = 0; j < NUMBER_OF_SLOTS(ptr); j++)
- gc_mark(THE_SLOT_OF(ptr, j));
- return;
- }
- case tc_next_method: gc_mark(CAR(ptr)); ptr = CDR(ptr); goto Top;
- #endif
- #ifdef USE_TK
- case tc_tkcommand: ptr = ptr->storage_as.tk.l_data; goto Top;
- #endif
- case tc_quote: return;
- case tc_lambda: return;
- case tc_if: return;
- case tc_setq: return;
- case tc_cond: return;
- case tc_and: return;
- case tc_or: return;
- case tc_let: return;
- case tc_letstar: return;
- case tc_letrec: return;
- case tc_begin: return;
- case tc_promise: ptr = ptr->storage_as.promise.expr; goto Top;
- case tc_apply:
- case tc_call_cc: return;
- case tc_dynwind: return;
- case tc_extend_env: return;
- case tc_unbound: return;
- default: if (EXTENDEDP(ptr)) {STk_extended_mark(ptr); return;}
- }
- /* if we are here, it's an implementation error. Signal it */
- fprintf(STk_stderr, "INTERNAL ERROR: trying to mark %lx (type=%d)\n",
- (unsigned long) ptr, TYPE(ptr));
- }
-
- static void gc_sweep(void)
- {
- SCM ptr, heap_org, heap_end, nfreelist;
- long n;
- int i;
-
- n = 0;
- nfreelist = NIL;
-
- for (i = 0; i < heaps_used; i++) {
- /* Sweep a heap */
- heap_org = heaps[i];
- heap_end = heap_org + heap_size;
-
- for (ptr=heap_org; ptr < heap_end; ptr++) {
- if (ptr->gc_mark == 0) {
- switch (TYPE(ptr)) {
- case tc_nil: break;
- case tc_cons: break;
- case tc_flonum: free(ptr->storage_as.flonum.data);
- case tc_integer: break;
- case tc_bignum: mpz_clear(BIGNUM(ptr)); free(BIGNUM(ptr)); break;
- case tc_symbol: STk_free_symbol(ptr); break;
- case tc_keyword: STk_free_keyword(ptr); break;
- case tc_subr_0: break;
- case tc_subr_1: break;
- case tc_subr_2: break;
- case tc_subr_3: break;
- case tc_subr_0_or_1: break;
- case tc_subr_1_or_2: break;
- case tc_subr_2_or_3: break;
- case tc_lsubr: break;
- case tc_ssubr: break;
- case tc_fsubr: break;
- case tc_syntax: break;
- case tc_closure: break;
- case tc_free_cell: break;
- case tc_char: break;
- case tc_string: free(ptr->storage_as.string.data); break;
- case tc_vector: free(ptr->storage_as.vector.data); break;
- case tc_eof: break;
- case tc_undefined: break;
- case tc_iport:
- case tc_oport: STk_freeport(ptr); break;
- case tc_isport:
- case tc_osport: STk_free_string_port(ptr); break;
- case tc_boolean: break;
- case tc_macro: break;
- case tc_localvar: break;
- case tc_globalvar: break;
- case tc_cont: free(ptr->storage_as.cont.data); break;
- case tc_env: break;
- case tc_address: break;
- case tc_autoload: break;
- case tc_Cpointer: if (!EXTSTATICP(ptr)) free(EXTDATA(ptr)); break;
- #ifdef USE_STKLOS
- case tc_instance: free(INST(ptr)); break;
- case tc_next_method: break;
- #endif
- #ifdef USE_TK
- case tc_tkcommand: if (ptr->storage_as.tk.data->Id[0])
- /* This object was renamed (rather than deleted) */
- Tcl_DeleteCommand(STk_main_interp,
- ptr->storage_as.tk.data->Id);
- free(ptr->storage_as.tk.data);
- break;
- #endif
- case tc_quote: break;
- case tc_lambda: break;
- case tc_if: break;
- case tc_setq: break;
- case tc_cond: break;
- case tc_and: break;
- case tc_or: break;
- case tc_let: break;
- case tc_letstar: break;
- case tc_letrec: break;
- case tc_begin: break;
- case tc_promise: break;
- case tc_apply: break;
- case tc_call_cc: break;
- case tc_dynwind: break;
- case tc_extend_env: break;
- case tc_unbound: break;
- default: if (EXTENDEDP(ptr))
- STk_extended_sweep(ptr);
- else
- fprintf(STk_stderr,
- "FATAL ERROR: trying to sweep %lx "
- "(type=%d)\n",
- (unsigned long) ptr, TYPE(ptr));
- }
-
- /* Declare this cell free and put it in free list */
- ptr->type = tc_free_cell;
- ptr->cell_info = 0;
- CDR(ptr) = nfreelist;
- nfreelist = ptr;
- n += 1;
- }
- else
- ptr->gc_mark = 0;
- }
- }
- gc_cells_collected = n;
- STk_freelist = nfreelist;
- }
-
- void STk_mark_stack(SCM *start, SCM *end)
- {
- register SCM p, heap_org;
- register long i, j, n;
-
- if (start > end) {
- SCM *tmp;
- tmp = start; start = end; end = tmp;
- }
- n = end - start;
-
- if (gc_verbose)
- fprintf(STk_stderr, "[Marking zone <0x%lx->0x%lx> (%ld words)]\n",
- (unsigned long) start, (unsigned long) end, (unsigned long) n);
- for(j=0; j<n; j++) {
- p = start[j];
- /* if p looks as a SCM pointer mark location */
- for (i=0; i < heaps_used; i++) {
- heap_org = heaps[i];
- if (VALID_ADDRESS(heap_org, p) && NTYPEP(p,tc_free_cell)) gc_mark(p);
- }
- }
- }
-
- static void mark_protected(void)
- {
- struct gc_protected *reg;
-
- /* Mark protected vars */
- for(reg = protected_registers; reg; reg = reg->next) gc_mark(*(reg->location));
-
- /* Mark all objects accessible from obarray */
- STk_mark_symbol_table();
-
- /* Mark the signal table */
- STk_mark_signal_table();
-
- /* Mark the table of traced variables */
- STk_mark_tracevar_table();
-
- #ifdef USE_TK
- /* Mark all Tcl/Tk callbacks */
- STk_mark_callbacks();
- #endif
- }
-
-
- static void gc_mark_and_sweep(void)
- {
- SCM stack_end; /* The topmost variable allocated on stack */
-
- gc_start();
- setjmp(save_regs_gc_mark);
- STk_mark_stack((SCM *) save_regs_gc_mark,
- (SCM *) (((char *) save_regs_gc_mark)+sizeof(save_regs_gc_mark)));
- mark_protected();
- STk_mark_stack((SCM *) STk_stack_start_ptr, (SCM *) &stack_end);
-
- gc_sweep();
- gc_end();
- }
-
- void STk_gc_for_newcell(void)
- {
- if (Error_context != ERR_FATAL) {
- STk_disallow_sigint();
- Error_context = ERR_FATAL;
- gc_mark_and_sweep();
- Error_context = ERR_OK;
- STk_allow_sigint();
- if (NNULLP(STk_freelist)) return;
- }
- Err("Out of storage",NIL);
- }
-
-
- PRIMITIVE STk_gc(void)
- {
- STk_disallow_sigint();
- Error_context = ERR_FATAL;
- gc_mark_and_sweep();
- Error_context = ERR_OK;
- STk_allow_sigint();
-
- return UNDEFINED;
- }
-
- void STk_gc_protect(SCM *location)
- {
- struct gc_protected *reg;
-
- reg = (struct gc_protected *) must_malloc(sizeof(struct gc_protected));
-
- reg->location = location;
- reg->next = protected_registers;
- protected_registers = reg;
- }
-
- void STk_gc_unprotect(SCM *location)
- {
- struct gc_protected *reg, *prev;
-
- for (prev=NULL, reg=protected_registers; reg; prev=reg, reg=reg->next)
- if (reg->location == location) {
- if (prev)
- prev->next = reg->next;
- else
- protected_registers = reg->next;
- free(reg);
- return;
- }
- }
-
-
- PRIMITIVE STk_gc_stats(void)
- {
- int i, freq[tc_stop_extd+1];
- register SCM ptr, heap_org, heap_end;
- long used_cells = 0L;
-
- /* Reset array of frequencies */
- for (i=0; i <=tc_stop_extd; i++) freq[i] = 0;
-
- /* Fill the frequencies array */
- for (i=0; i < heaps_used; i++) {
- heap_org = heaps[i];
- heap_end = heap_org + heap_size;
-
- for(ptr = heap_org; ptr < heap_end; ptr++) {
- if (NTYPEP(ptr, tc_free_cell)) {
- used_cells += 1;
- freq[TYPE(ptr)] += 1;
- }
- }
- }
-
- /* Print statistics */
- fprintf(STk_stderr, ";; GC statistics\n");
- fprintf(STk_stderr, ";; -------------\n");
- fprintf(STk_stderr, ";; cells used %ld/%ld\n", used_cells, heaps_used*heap_size);
- fprintf(STk_stderr, ";; # of used heaps %d\n", heaps_used);
- fprintf(STk_stderr, ";; # of GC calls %d (time spent in GC %.2fms)\n",
- gc_calls, STk_total_gc_time);
-
- for (i=0; i <= tc_stop_extd; i++)
- if (freq[i]) fprintf(STk_stderr, "(%d %d) ", i, freq[i]);
- fprintf(STk_stderr, "\n;;\n");
-
- return UNDEFINED;
- }
-
-
- PRIMITIVE STk_find_cells(SCM type)
- {
- SCM ptr, z, heap_org, heap_end;
- int i,t,l;
-
- if (NINTEGERP(type)) Err("%find-cells: bad integer", type);
- t = INTEGER(type);
-
- /* Count how many items we have */
- for (i=l=0; i < heaps_used; i++) {
- heap_org = heaps[i];
- heap_end = heap_org + heap_size;
-
- for (ptr=heap_org; ptr < heap_end; ptr++)
- if (TYPEP(ptr, t)) l += 1;
- }
-
- /* Allocate a vector for the result */
- z = STk_makevect(l, NULL);
- l = 0;
-
- /* Place all the items in the newly created vector */
- for (i=l=0; i < heaps_used; i++) {
- heap_org = heaps[i];
- heap_end = heap_org + heap_size;
-
- for (ptr=heap_org; ptr < heap_end; ptr++)
- if (ptr != z && TYPEP(ptr, t))
- VECT(z)[l++] = ptr;
- }
- return z;
- }
-
- PRIMITIVE STk_expand_heap(SCM arg)
- {
- int i, number_of_heaps, wanted;
-
- if (NINTEGERP(arg)) Err("expand-heap: bad integer", arg);
-
- gc_verbose = VCELL(Intern(GC_VERBOSE))!=Ntruth;
- wanted = INTEGER(arg);
- number_of_heaps = (wanted + heap_size - 1) / heap_size;
-
- for (i = heaps_used; i < number_of_heaps; i++)
- allocate_new_heap();
- return UNDEFINED;
- }
-
- #ifndef max
- #define max(a,b) (((a)<(b))?(b):(a))
- #endif
-
- void STk_init_gc(void)
- {
- STk_freelist = NIL;
- if (STk_arg_cells) {
- /* Set the heap size to the specified value */
- int tmp = atoi(STk_arg_cells);
- if (tmp > 0) heap_size = max(tmp,MIN_HEAP);
- }
- allocate_new_heap();
- }
-